#include <iostream>
#include <string>
#include <sys/epoll.h>
#include "tcp_socket.hh"
class epoll_server_t
{
public:
    static const int events_max = 1024;

public:
    epoll_server_t(uint16_t port)
    {
        // socket
        _listen_fd = get_socket_fd(port);
        if (_listen_fd < 0)
        {
            std::cerr << strerror(errno) << std::endl;
            exit(1);
        }
        // epoll_create
        _epoll_fd = epoll_create1(0);
        if (_epoll_fd < 0)
        {
            std::cerr << strerror(errno) << std::endl;
            exit(2);
        }
        // add listenfd to epoll to wait for reading
        epoll_event lev;
        lev.events = EPOLLIN;
        lev.data.fd = _listen_fd;
        if (epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, _listen_fd, &lev))
        {
            std::cerr << strerror(errno) << std::endl;
            exit(3);
        }
    }
    ~epoll_server_t()
    {
        // close fds
        close(_epoll_fd);
        close(_listen_fd);
    }
    void run()
    {
        // wait result
        epoll_event rev[events_max];
        // time to wait
        int timeout = 5000;
        listen(_listen_fd, 5);
        // loop
        while (true)
        {
            int ready_num = epoll_wait(_epoll_fd, rev, events_max, timeout);
            if (ready_num == 0)
            {
                // other work
                //...
                continue;
            }
            // handle events
            events_handler(rev, ready_num);
        }
    }

private:
    bool events_handler(epoll_event *evt_arr, size_t ready_num)
    {
        // handle fds which are ready for I/O
        for (int i = 0; i < ready_num; i++)
        {
            epoll_event evt = evt_arr[i];
            // check I
            if (evt.events & EPOLLIN)
            {
                // accept
                if (evt.data.fd == _listen_fd)
                {
                    sockaddr_in peer;
                    socklen_t socklen;
                    int newfd = accept(_listen_fd, (sockaddr *)&peer, &socklen);
                    if (newfd < 0)
                    {
                        return false;
                    }
                    epoll_event event_add;
                    event_add.data.fd = newfd;
                    event_add.events = EPOLLIN;
                    if (epoll_ctl(_epoll_fd, EPOLL_CTL_ADD, newfd, &event_add))
                        return false;
                }
                // read
                else
                {
                    // buffer
                    char buffer[1024];
                    bzero(buffer, sizeof buffer);
                    // cpoy data
                    ssize_t read_len = read(evt.data.fd, buffer, sizeof buffer - 1);
                    // check END OF FILE
                    if (read_len == 0)
                    {
                        int ctl_ret = epoll_ctl(_epoll_fd, EPOLL_CTL_DEL, evt.data.fd, &evt);
                        close(evt.data.fd);
                        if (ctl_ret)
                            return false;
                    }
                    // add '\0'
                    buffer[read_len] = '\0';
                    std::cout << std::string(buffer);
                }
            }
            // check O
            // TODO...
        }
        return true;
    }

private:
    int _epoll_fd;
    int _listen_fd;
};